#include <buola/gui/x11/cx11dispatcher.h>

#include <buola/gui/x11/xembed.h>
#include <buola/app/ceventloop.h>
#include <buola/gui/cwindow.h>
#include <buola/gui/cconfig.h>
#include <buola/gui/cwindowmanager.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11toolkit.h>
#include <buola/gui/x11/cx11sync.h>
#include <buola/gui/x11/cx11window.h>
#include <buola/gui/x11/cx11clipboard.h>
#include <buola/gui/x11/cx11windowwm.h>
#include <buola/io/cfdpoller.h>

namespace buola { namespace gui { namespace x11 {

static int sLastEvent=LASTEvent;

struct SEventInfo
{
    bool mIgnore;       //we don't knot about this event. always ignore it
    bool mHasWindow;    //has a window associated
    bool mIgnoreIfNoWindow; //ignore it if the window doesn't exist
};
/*
static SEventInfo sEventInfo[]=
{
    {   true,   false,  false   },          //0 (no event)
    {   true,   false,  false   },          //1 (no event)
    {   false,  true,   false   },          //2 (no event)

};
*/

CX11Dispatcher::CX11Dispatcher(CX11Display *pDisplay,CX11Toolkit *pToolkit)
{
    mDisplay=pDisplay;
    mToolkit=pToolkit;

    XSetErrorHandler(ErrorHandler);
    XSetIOErrorHandler(IOErrorHandler);

    get_main_event_loop().AddFunction(CEventLoop::PRIORITY_NORMAL,this);
    io::get_main_fd_poller().AddFDListener(this,mDisplay->GetConnectionNumber(),
                                        io::ENotify::READ);
    mOptions=OPTION_NONE;
    mLastClickWindow=nullptr;
    mLastClickTime=0;
    mLastClickCount=0;
}

////////////////////////////////
// error handlers for display
//

int CX11Dispatcher::ErrorHandler(Display *pDisplay,XErrorEvent *pEvent)
{
    if(pEvent->error_code==get_x11_toolkit()->mDamageError+BadDamage)
    {
        msg_error() << "BadDamage error\n";
        return 0;
    }
    else if(pEvent->error_code==BadWindow)
    {
        if(!get_x11_toolkit()->GetDispatcher()->DispatchBadWindow(pEvent->resourceid))
            return 0;
    }

    char lMsg[100];
    XGetErrorText(pDisplay,pEvent->error_code,lMsg,100);
    back_trace();
    msg_error() << "X11 error " << lMsg << ", serial " << pEvent->serial <<
                ", opcode " << (int)pEvent->request_code << ", ID " <<
                pEvent->resourceid << "\n";

    return 0;
}

int CX11Dispatcher::IOErrorHandler(Display*)
{
    fatal_error("Lost connection to server\n");
    return 0;
}

////////////////////////////////////////////////////////////////////
//////////////////                               /////////////////
///////////////////    Event dispatching      ///////////////////
//////////////////                              //////////////////
////////////////////////////////////////////////////////////////////

EMouseButton CX11Dispatcher::TranslateButtonFromX(unsigned int pX)
{
    switch(pX)
    {
    case Button1:
        return EMouseButton::LEFT;
    case Button2:
        return EMouseButton::MIDDLE;
    case Button3:
        return EMouseButton::RIGHT;
    case 8:
        return EMouseButton::BACK;
    case 9:
        return EMouseButton::FORWARD;
    default:
        return EMouseButton::NONE;
    }
}

unsigned int CX11Dispatcher::TranslateButtonToX(EMouseButton pX)
{
    switch(pX)
    {
    case EMouseButton::LEFT:
        return Button1;
    case EMouseButton::MIDDLE:
        return Button2;
    case EMouseButton::RIGHT:
        return Button3;
    case EMouseButton::BACK:
        return 8;
    case EMouseButton::FORWARD:
        return 9;
    default:
        return None;
    }
}

void CX11Dispatcher::DispatchEvent(XEvent *pE)
{
    for(int i=0;i<mFilters.size();i++)
    {
        if(!mFilters[i]->OnX11Event(pE)) return;
    }

    if(pE->type<sLastEvent)         //normal X event
    {
        CX11EventFilter *lExternal=mExternalWindowFilters[pE->xany.window];
        if(lExternal)
        {
            if(!lExternal->OnX11Event(pE)) return;
        }

        switch(pE->type)
        {
        case Expose:
            DispatchExposeEvent(&pE->xexpose);
            return;

        case KeyPress:
        case KeyRelease:
            DispatchKeyEvent(&pE->xkey);
            return;

        case ButtonPress:
        case ButtonRelease:
            DispatchButtonEvent(&pE->xbutton);
            return;

        case MotionNotify:
            DispatchMotionEvent(&pE->xmotion);
            return;

        case EnterNotify:
        case LeaveNotify:
            DispatchCrossingEvent(&pE->xcrossing);
            return;

        case ConfigureNotify:
            DispatchConfigureEvent(&pE->xconfigure);
            return;

        case FocusIn:
        case FocusOut:
            DispatchFocusEvent(&pE->xfocus);
            return;

        case CreateNotify:
            DispatchCreateEvent(&pE->xcreatewindow);
            return;

        case DestroyNotify:
            DispatchDestroyEvent(&pE->xdestroywindow);
            return;

        case MapNotify:
            DispatchMapEvent(&pE->xmap);
            return;

        case UnmapNotify:
            DispatchUnmapEvent(&pE->xunmap);
            return;

        case ClientMessage:
            DispatchClientMessageEvent(&pE->xclient);
            return;

        case SelectionRequest:
            DispatchSelectionRequestEvent(&pE->xselectionrequest);
            return;

        case SelectionClear:
            DispatchSelectionClearEvent(&pE->xselectionclear);
            return;

        case SelectionNotify:
            DispatchSelectionNotifyEvent(&pE->xselection);
            return;

        case GraphicsExpose:
            DispatchGraphicsExposeEvent(&pE->xgraphicsexpose);
            return;

        case MapRequest:
        case ConfigureRequest:
        case MappingNotify:
        case PropertyNotify:
        case ReparentNotify:
        case GravityNotify:
        case NoExpose:
            DispatchFilterEvent(pE);
            return;
        }
    }
    else if(pE->type==mToolkit->mShapeEvent+ShapeNotify)
    {
        CX11EventFilter *lExternal=mExternalWindowFilters[pE->xany.window];
        if(lExternal)
        {
            if(!lExternal->OnX11ShapeEvent((XShapeEvent*)pE)) return;
        }

        DispatchShapeEvent(pE);
    }
    else if(pE->type==mToolkit->mDamageEvent+XDamageNotify)
    {
        DispatchDamageEvent(pE);
    }
    else if(pE->type==CX11SyncCounter::mEvent+XSyncAlarmNotify)
    {
        DispatchAlarm(pE);
    }
    else
    {
        msg_debug<25>() << "Unknown event " << pE->type << "\n";
    }
}

void CX11Dispatcher::DispatchNextEvent()
{
    XEvent lEvent;
    XNextEvent(mDisplay->GetDisplay(),&lEvent);
    DispatchEvent(&lEvent);
}

void CX11Dispatcher::AddFilter(CX11EventFilter *pFilter)
{
    mFilters.push_back(pFilter);
}

void CX11Dispatcher::RemoveFilter(CX11EventFilter *pFilter)
{
    mFilters.erase(std::find(mFilters.begin(),mFilters.end(),pFilter));
}

void CX11Dispatcher::AddClientMessageFilter(CX11EventFilter *pFilter)
{
    mClientMessageFilters.push_back(pFilter);
}

void CX11Dispatcher::RemoveClientMessageFilter(CX11EventFilter *pFilter)
{
    mClientMessageFilters.erase(std::find(mClientMessageFilters.begin(),
                mClientMessageFilters.end(),pFilter));
}

void CX11Dispatcher::AddDamageFilter(CX11EventFilter *pFilter)
{
    mDamageFilters.push_back(pFilter);
}

void CX11Dispatcher::RemoveDamageFilter(CX11EventFilter *pFilter)
{
    mDamageFilters.erase(std::find(mDamageFilters.begin(),
                mDamageFilters.end(),pFilter));
}

void CX11Dispatcher::AddExternalWindowFilter(Window pID,
                                        CX11EventFilter *pFilter)
{
    mExternalWindowFilters[pID]=pFilter;
}

void CX11Dispatcher::RemoveExternalWindowFilter(Window pID,
                                        CX11EventFilter*)
{
    mExternalWindowFilters.erase(pID);
}

void CX11Dispatcher::SetOptions(EOptions pOptions)
{
    mOptions=pOptions;
}

void CX11Dispatcher::DispatchExposeEvent(XExposeEvent *pE)
{
    //find target window
    CX11Window *lXW=CX11Window::GetX11Window(pE->window);

    if(!lXW)
    {
        msg_debug<25>() << "Expose event for non-existing window\n";
        return;
    }

    CWindow *lW=lXW->GetWindow().get();

    if(!lW) return;

    //decide whether it should really receive it
    if(lXW->GetFlag(CX11Window::FLAG_RESIZEPENDING)) return;

    window_manager().OnPhysicalExpose(lXW,CRect_d(CPoint_d(pE->x,pE->y),CSize_d(pE->width,pE->height)),
                                      pE->count,pE->send_event);
    if(lXW->mWM&&lXW->mWM->mSyncRequest)
        lXW->mWM->SyncFinished();
}

void CX11Dispatcher::DispatchGraphicsExposeEvent(XGraphicsExposeEvent *pE)
{
    //find target window
    CX11Window *lXW=CX11Window::GetX11Window(pE->drawable);

    if(!lXW)
    {
        msg_debug<25>() << "Graphics expose event for non-existing window\n";
        return;
    }

    CWindow *lW=lXW->GetWindow().get();

    if(!lW) return;

    //decide whether it should really receive it
    if(lXW->GetFlag(CX11Window::FLAG_RESIZEPENDING)) return;

    window_manager().OnPhysicalExpose(lXW,CRect_d(CPoint_d(pE->x,pE->y),CSize_d(pE->width,pE->height)),
                                      pE->count,pE->send_event);
}

void CX11Dispatcher::DispatchKeyEvent(XKeyEvent *pE,bool /*pForce*/)
{
    mToolkit->mLastTimeStamp=pE->time;

    if(XFilterEvent((XEvent*)pE,None)) //filtered by IC
        return;

    CKeyEvent lE;

    KeySym lKeySym;

    lKeySym=XLookupKeysym(pE,0);

    lE.mKey=CX11Toolkit::GetKey(lKeySym);
    lE.mMods=CX11Toolkit::GetMods(pE->state);
    lE.mLocks=CX11Toolkit::GetLocks(pE->state);
    lE.mPlainChar=false;
    lE.mChar=0;

    //Map key to SymKey and maybe char
    if(pE->type==KeyPress)
    {
        char lChar[8];
        int lLen=0;
        
        if(mDisplay->GetXIC())
        {
            Status lStatus;
            msg_info() << "in dispatch key event " << (void*)mDisplay->GetXIC() << " " << (void*)pE << "\n";
            lLen=Xutf8LookupString(mDisplay->GetXIC(),pE,lChar,8,
                                                &lKeySym,&lStatus);
            msg_info() << "2\n";
            if(lLen)
            {
                lChar[lLen]=0;
                lE.mChar=utf32(lChar)[0];
            }
        }
        else
        {
            lLen=XLookupString(pE,lChar,8,&lKeySym,nullptr);
            lE.mChar=lChar[0];
        }

        lE.mType=CKeyEvent::PRESS;

        if(lE.mChar>=32&&lE.mChar!=127&&!(lE.mMods&(KEYMOD_ALT|KEYMOD_CONTROL)))
        {
            lE.mPlainChar=true;
        }
    }
    else
    {
        lE.mType=CKeyEvent::RELEASE;
    }

    window_manager().OnPhysicalKey(&lE);
}

void CX11Dispatcher::DispatchButtonEvent(XButtonEvent *pE)
{
    mToolkit->mLastTimeStamp=pE->time;

    //find target window
    CPoint_d lPoint(pE->x,pE->y);

    CX11Window *lTarget=CX11Window::GetX11Window(pE->window);

    if(!lTarget)
    {
        msg_debug<15>() << " Button event for non-existing window\n";
        return;
    }

    //create the event
    CMouseEvent lE;

    lE.mMods=CX11Toolkit::GetMods(pE->state);
    lE.mLocks=CX11Toolkit::GetLocks(pE->state);
    lE.mButtons=CX11Toolkit::GetButtons(pE->state);

    if(pE->button==Button4||pE->button==Button5)
    {
        if(pE->type!=ButtonPress) return;
        lE.mButton=EMouseButton::NONE;
        
        lE.mType=CMouseEvent::WHEEL;
        lE.mScrollX=0;
        lE.mScrollY=(pE->button==Button4)?-1:1;
        lE.mScrollZ=0;
    }
    else if(pE->type==ButtonPress)
    {
        lE.mType=CMouseEvent::PRESS;
        lE.mButton=TranslateButtonFromX(pE->button);
        lE.mButtons|=lE.mButton;
        lE.mClickCount=1;
        
        if(lE.mButton==EMouseButton::NONE) return;
    }
    else
    {
        lE.mType=CMouseEvent::RELEASE;
        lE.mButton=TranslateButtonFromX(pE->button);
        lE.mButtons&=~lE.mButton;
        lE.mClickCount=0;
        
        if(lE.mButton==EMouseButton::NONE) return;
    }

    lE.mPoint=lPoint;
    lE.mRootPoint.x=pE->x_root;
    lE.mRootPoint.y=pE->y_root;

    if(lE.mType==CMouseEvent::PRESS) //if it is a press, check for multiple clicks
    {
        //double-click checking
        if(chrono::milliseconds(pE->time-mLastClickTime)<
                        gConfig.mMouse.GetDoubleClickTime()&&
                lE.mButton==mLastClickButton&&lTarget==mLastClickWindow&&
                dist(mLastClickPos,lE.mRootPoint)<=
                        gConfig.mMouse.GetDoubleClickDistance()&&mLastClickCount<4)
        {
            mLastClickCount++;
            mLastClickTime=pE->time;
            lE.mClickCount=mLastClickCount;
        }
        else
        {
            mLastClickWindow=lTarget;
            mLastClickTime=pE->time;
            mLastClickButton=lE.mButton;
            mLastClickPos=lE.mRootPoint;
            mLastClickCount=1;
        }
    }

    window_manager().OnPhysicalMouse(&lE,lTarget);
}

void CX11Dispatcher::DispatchMotionEvent(XMotionEvent *pE)
{
    mToolkit->mLastTimeStamp=pE->time;

    CX11Window *lTarget=CX11Window::GetX11Window(pE->window);

    if(!lTarget)
    {
        msg_debug<25>() << "Motion event for non-existing window\n";
        return;
    }

    if(!lTarget->GetWindow()) return;

    CMouseEvent lE;
    lE.mType=CMouseEvent::MOTION;

    mToolkit->QueryPointer(lTarget,lE.mPoint,lE.mRootPoint);

    if(lE.mRootPoint.x!=pE->x_root||lE.mRootPoint.y!=pE->y_root)
        return;

    lE.mMods=CX11Toolkit::GetMods(pE->state);
    lE.mLocks=CX11Toolkit::GetLocks(pE->state);
    lE.mButtons=CX11Toolkit::GetButtons(pE->state);
    lE.mClickCount=0;

    window_manager().OnPhysicalMouse(&lE,lTarget);
}

void CX11Dispatcher::DispatchCrossingEvent(XCrossingEvent *pE)
{
    mToolkit->mLastTimeStamp=pE->time;

    //find target window
    CPoint_d lPoint(pE->x,pE->y);

    CX11Window *lTarget=CX11Window::GetX11Window(pE->window);

    if(!lTarget)
    {
        msg_debug<25>() << "Crossing event for non-existing window\n";
        return;
    }

    if(pE->mode==NotifyGrab) return;

    CMouseEvent lE;
    lE.mMods=((EKeyMod)pE->state)&KEYMOD_ALL;
    lE.mLocks=((EKeyLock)pE->state)&KEYLOCK_ALL;
    lE.mButtons=CX11Toolkit::GetButtons(pE->state);
    lE.mPoint=lPoint;
    lE.mRootPoint.x=pE->x_root;
    lE.mRootPoint.y=pE->y_root;
    lE.mClickCount=0;

    if(pE->type==EnterNotify)
    {
        lE.mType=CMouseEvent::MOVEIN;
    }
    else if(pE->type==LeaveNotify)
    {
        lE.mType=CMouseEvent::MOVEOUT;
    }
    window_manager().OnPhysicalMouse(&lE,lTarget);
}

void CX11Dispatcher::DispatchConfigureEvent(XConfigureEvent *pE)
{
    if(pE->event==pE->window)   //sent to the same window
    {
        CX11Window *lTarget=CX11Window::GetX11Window(pE->window);

        if(!lTarget)
        {
            msg_debug<5>() << "Configure event for non-existing window\n";
            return;
        }

        if(!lTarget->GetWindow()) return;

        Window lRoot;
        CPoint_d lPos(pE->x,pE->y);
        CSize_d lSize(pE->width,pE->height);

        int lX=pE->x,lY=pE->y;
        unsigned int lWidth=pE->width,lHeight=pE->height,lBorderWidth,lDepth;

        if(lSize!=lTarget->GetSize()||lPos!=lTarget->GetPos())
        {
            XGetGeometry(mDisplay->GetDisplay(),pE->window,&lRoot,&lX,&lY,
                        &lWidth,&lHeight,&lBorderWidth,&lDepth);
        }

        bool lReport=false;
        
        if(lPos!=lTarget->GetPos()||lTarget->GetFlag(CX11Window::FLAG_MOVEPENDING))
        {
            //Sometimes pos=0 not true
            if((!lTarget->IsWindowManaged())||lPos.x!=0||lPos.y!=0)
            {
                lTarget->UnsetFlag(CX11Window::FLAG_MOVEPENDING);
                lTarget->mPos=lPos;
                lReport=true;
            }
        }
        if(lSize!=lTarget->GetSize()||lTarget->GetFlag(CX11Window::FLAG_RESIZEPENDING))
        {
            if((int)lWidth==pE->width&&(int)lHeight==pE->height)
            {
                lTarget->UnsetFlag(CX11Window::FLAG_RESIZEPENDING);
                lTarget->mSize=lSize;
                lReport=true;
            }
        }
        if(lReport)
            window_manager().OnPhysicalGeometry(lTarget,lTarget->GetPos(),lTarget->GetSize());
    }
    else
    {
        DispatchFilterEvent((XEvent*)pE);
    }
}

void CX11Dispatcher::DispatchFocusEvent(XFocusChangeEvent *pE)
{
    //find target window
    CX11Window *lW=CX11Window::GetX11Window(pE->window);

    if(!lW)
    {
        msg_debug<25>() << "Focus event for non-existing window\n";
        return;
    }

    if(!lW->GetWindow()) return;

    msg_debug<25>() << "FocusEvent " << pE->type << " " <<
            pE->mode << " " << pE->detail << " " <<
            pE->window << "\n";

    if(pE->detail==NotifyPointer||pE->detail==NotifyPointerRoot||
        pE->detail==NotifyDetailNone||pE->detail==NotifyVirtual||
        pE->detail==NotifyNonlinearVirtual) return;

    if(pE->mode==NotifyGrab||pE->mode==NotifyUngrab)
    {
        ///\todo check what really happens here
        if(pE->mode==NotifyGrab&&pE->type==FocusIn&&pE->detail!=NotifyAncestor)
        {
            msg_info() << "setting!!\n";
            window_manager().SetGrabFocusTo(lW->GetWindow().get());
        }
        if(pE->type==FocusOut&&pE->detail!=NotifyAncestor)
        {
            msg_info() << "unsetting!!\n";
            window_manager().SetGrabFocusTo(nullptr);
        }
        return;
    }
    else
    {
        if(pE->type==FocusIn)
        {
            msg_debug<25>() << "Notifying activated\n";
            window_manager().OnPhysicalActivate(lW,true);
        }
        else if(pE->type==FocusOut)
        {
            window_manager().OnPhysicalActivate(lW,false);
        }
    }
}

void CX11Dispatcher::DispatchFilterEvent(XEvent *pE)
{
    CX11Window *lW=CX11Window::GetX11Window(pE->xany.window);

    if(!lW)
    {
        msg_debug<15>() << "filter event for non-existing window" <<
                pE->xany.window << " event " << pE->type << "\n";
        return;
    }

    for(int i=0;i<lW->mFilters.size();i++)
    {
        if(!lW->mFilters[i]->OnX11Event(pE)) return;
    }
}

void CX11Dispatcher::DispatchShapeEvent(XEvent *pE)
{
    CX11Window *lW=CX11Window::GetX11Window(pE->xany.window);

    if(!lW)
    {
        msg_debug<5>() << "shape event for non-existing window\n";
        return;
    }

    for(int i=0;i<lW->mFilters.size();i++)
    {
        if(!lW->mFilters[i]->OnX11ShapeEvent((XShapeEvent*)pE))
            return;
    }
}

void CX11Dispatcher::DispatchDamageEvent(XEvent *pE)
{
    for(int i=0;i<mDamageFilters.size();i++)
    {
        if(!mDamageFilters[i]->OnX11DamageEvent((XDamageNotifyEvent*)pE)) return;
    }
}

void CX11Dispatcher::DispatchCreateEvent(XCreateWindowEvent *pE)
{
    DispatchFilterEvent((XEvent*)pE);

    CX11Window *lTarget=CX11Window::GetX11Window(pE->parent);
    CX11Window *lChild=CX11Window::GetX11Window(pE->window);

    if(!lTarget||!lChild) return;

    if(!lTarget->GetWindow()||!lChild->GetWindow()) return;

msg_warn() << "should dispatch child event\n";
    //lTarget->GetWindow()->OnPhysicalChild(lChild->GetWindow(),CHILD_CREATE);
}

void CX11Dispatcher::DispatchDestroyEvent(XDestroyWindowEvent *pE)
{
    CX11Window *lTarget=CX11Window::GetX11Window(pE->event);

    if(pE->window==pE->event)
    {
        //event sent to the destroyed window
        if(lTarget&&lTarget->IsForeign())
        {
msg_warn() << "should dispatch destroy event\n";
//            if(lTarget->GetWindow())
  //              lTarget->GetWindow()->OnPhysicalDestroy();
        }
    }
    else
    {
        DispatchFilterEvent((XEvent*)pE);

        CX11Window *lChild=CX11Window::GetX11Window(pE->window);

        if(!lTarget||!lChild) return;

        if(!lTarget->GetWindow()||!lChild->GetWindow()) return;

msg_warn() << "should dispatch child event\n";
//        lTarget->GetWindow()->OnPhysicalChild(lChild->GetWindow(),CHILD_DESTROY);
    }
}

void CX11Dispatcher::DispatchMapEvent(XMapEvent *pE)
{
    //find target window
    CX11Window *lTarget=CX11Window::GetX11Window(pE->event);

    if(!lTarget)
    {
        msg_debug<25>() << "Map event for non-existing window\n";
        return;
    }

    if(pE->event==pE->window)
    {
        if(lTarget->GetWindow())
            window_manager().OnPhysicalMap(lTarget,true);
    }
    else
    {
        DispatchFilterEvent((XEvent*)pE);

        CX11Window *lChild=CX11Window::GetX11Window(pE->window);

        if(!lChild)
        {
            msg_debug<25>() << "Map event for non-existing window\n";
            return;
        }

        if(lChild->GetWindow())
            window_manager().OnPhysicalMap(lChild,true);
    }
}

void CX11Dispatcher::DispatchUnmapEvent(XUnmapEvent *pE)
{
    //find target window
    CX11Window *lTarget=CX11Window::GetX11Window(pE->event);

    if(!lTarget)
    {
        msg_debug<25>() << "Unmap event for non-existing window\n";
        return;
    }

    if(pE->event==pE->window)
    {
        if(lTarget->GetWindow())
            window_manager().OnPhysicalMap(lTarget,false);
    }
    else
    {
        DispatchFilterEvent((XEvent*)pE);

        CX11Window *lChild=CX11Window::GetX11Window(pE->window);

        if(!lChild)
        {
            msg_debug<25>() << "Unmap event for non-existing child\n";
            return;
        }
        if(lChild->GetWindow())
            window_manager().OnPhysicalMap(lChild,false);
    }
}

void CX11Dispatcher::DispatchSelectionRequestEvent(XSelectionRequestEvent *pE)
{
    int lType=CX11Clipboard::AtomToType(pE->selection);
    CClipboard *lCB=mToolkit->GetClipboard(lType);
    ((CX11Clipboard*)lCB)->ProcessSelectionRequest(pE);
}

void CX11Dispatcher::DispatchSelectionClearEvent(XSelectionClearEvent *pE)
{
    int lType=CX11Clipboard::AtomToType(pE->selection);
    CClipboard *lCB=mToolkit->GetClipboard(lType);
    ((CX11Clipboard*)lCB)->ProcessSelectionClear(pE);
}

void CX11Dispatcher::DispatchSelectionNotifyEvent(XSelectionEvent *pE)
{
    int lType=CX11Clipboard::AtomToType(pE->selection);
    CClipboard *lCB=mToolkit->GetClipboard(lType);
    ((CX11Clipboard*)lCB)->ProcessSelectionNotify(pE);
}

void CX11Dispatcher::DispatchClientMessageEvent(XClientMessageEvent *pE)
{
    for(int i=0;i<mClientMessageFilters.size();i++)
    {
        if(!mClientMessageFilters[i]->OnX11ClientMessage(pE)) return;
    }

    //find target window
    CX11Window *lW=CX11Window::GetX11Window(pE->window);

    if(!lW)
    {
        msg_debug<25>() << "Client event for non-existing window\n";
        return;
    }

    if(pE->message_type==get_default_x11_display()->Atoms().mProtocols)
    {
        if((Atom)pE->data.l[0]==get_default_x11_display()->Atoms().mDeleteWindow)
        {
            if(lW->GetWindow())
                window_manager().OnPhysicalCloseWindow(lW);
            return;
        }
        else if((Atom)pE->data.l[0]==get_default_x11_display()->Atoms().mTakeFocus)
        {
            msg_debug<25>() << "Received take focus!!\n";
        }
        else if((Atom)pE->data.l[0]==get_default_x11_display()->Atoms().mNetWMSyncRequest)
        {
            if(!lW->IsWindowManaged())
            {
                msg_warn() << "Received SyncRequest for not managed window\n";
                return;
            }
            lW->mWM->SyncRequested(((int64_t)pE->data.l[2])+
                        (((int64_t)pE->data.l[3])<<32));
        }
    }
    else if(pE->message_type==get_default_x11_display()->Atoms().mXEmbed)
    {
        if(pE->data.l[1]==XEMBED_EMBEDDED_NOTIFY)
        {
            lW->SetFlag(CX11Window::FLAG_EMBEDDED);
        }
        else if(pE->data.l[1]==XEMBED_WINDOW_ACTIVATE)
        {
            if(lW->GetWindow())
                window_manager().OnPhysicalActivate(lW,true);
        }
        else if(pE->data.l[1]==XEMBED_WINDOW_DEACTIVATE)
        {
            if(lW->GetWindow())
                window_manager().OnPhysicalActivate(lW,false);
        }
        else if(pE->data.l[1]==XEMBED_REQUEST_FOCUS)
        {
            lW->OnXEmbedRequestFocus();
        }
        else
        {
            msg_warn() << "Embedded unknown " << pE->data.l[1] << "\n";
        }
    }
    else
    {
        for(int i=0;i<lW->mFilters.size();i++)
        {
            if(!lW->mFilters[i]->OnX11ClientMessage(pE)) return;
        }
        msg_warn() << "Received unprocessed client message " <<
                    mDisplay->GetAtomName(pE->message_type) << "\n";
    }
}

void CX11Dispatcher::DispatchAlarm(XEvent *pE)
{
    XSyncAlarmNotifyEvent *lE=(XSyncAlarmNotifyEvent*)pE;
    if(lE->state!=XSyncAlarmActive)
        return;

    const std::vector<CX11SyncAlarm*> &lAll=CX11SyncAlarm::GetAll();

    for(int i=0;i<lAll.size();i++)
    {
        if(lAll[i]->GetID()==(TID)lE->alarm)
        {
            lAll[i]->Trigger();
            return;
        }
    }

    msg_warn() << "Missed alarm notify " << XSyncValueLow32(lE->counter_value) <<
        " wanted " << XSyncValueLow32(lE->alarm_value) << " " << (int)lE->state << "\n";
}

bool CX11Dispatcher::DispatchBadWindow(Window pID)
{
    for(int i=0;i<mFilters.size();i++)
    {
        if(!mFilters[i]->OnX11BadWindow(pID)) return false;
    }

    CX11Window *lW=CX11Window::GetX11Window(pID);

    if(!lW)
        return true;

    for(int i=0;i<lW->mFilters.size();i++)
    {
        if(!lW->mFilters[i]->OnX11BadWindow(pID)) return false;
    }

    return true;
}

void CX11Dispatcher::DispatchPending(CX11Window *pWindow,int pType)
{
    XEvent lReturn;

    while(XCheckTypedWindowEvent(mDisplay->GetDisplay(),pWindow->GetX11ID(),
                                    pType,&lReturn))
    {
        DispatchEvent(&lReturn);
    }
}

bool CX11Dispatcher::OnLoopFunction()
{
    if(!QLength(mDisplay->GetDisplay()))
    {
        XFlush(mDisplay->GetDisplay());
        return true;
    }

    if(mOptions&OPTION_PROCESSALL)
    {
        do
        {
            DispatchNextEvent();
        } while(QLength(mDisplay->GetDisplay()));
    }
    else
    {
        DispatchNextEvent();
    }

    return false;   //don't do select
}

void CX11Dispatcher::OnFDListener(int,io::ENotify,void*)
{
    while(XEventsQueued(get_default_x11_display()->GetDisplay(),QueuedAfterFlush))
        DispatchNextEvent();
}

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }
